﻿using Devil;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GameToolkit
{
    public static class CacheUtil
    {
        class CacheList
        {
            public readonly int id;
            public readonly IList list;
            public int hot;
            public CacheList(int id, IList lst)
            {
                this.id = id;
                this.list = lst;
            }
        }

        //static object sLock = new object();

        static SortedList<CacheList> sCacheLst;

        [System.Obsolete("This method will be removed in the future, Use CachedList<T>.GetList() and CachedList<T>.Release() instead of this.")]
        public static IList<T> GetTemporaryList<T>(int group = 0)
        {
            return GetListCahce<T>(group, true);
        }

        // max groupId: 7
        public static List<T> GetListCahce<T>(int groupId = 0, bool subThreadEnabled = true) // where T : class
        {
            if (!ParallelDispatcher.IsMainThread)
            {
                if (subThreadEnabled)
                    return new List<T>();
                else
                    throw new System.Exception("Get List Cache only invoke on main thread.");
            }
            //lock (sLock)
            //{
            if (sCacheLst == null)
                sCacheLst = new SortedList<CacheList>(32, (a) => a.id);
            var id = MathExt.Hash(typeof(T).GetHashCode(), groupId & 7);
            var cache = sCacheLst.GetValue(id);
            if (cache == null)
            {
                var lst = new List<T>(32);
                cache = new CacheList(id, lst);
                cache.hot = 10;
                sCacheLst.Add(cache);
                return lst;
            }
            else
            {
                if (cache.hot < int.MaxValue)
                    cache.hot++;
                var lst = (List<T>)cache.list;
                lst.Clear();
                return lst;
            }
            //}
        }

        static RaycastHit[] sRaycastCache;
        static RaycastHit[] sRaycastCache2;
        public static RaycastHit[] RaycastCache
        {
            get
            {
                if (!MainThread.IsMainThread)
                    throw new System.Exception("Raycast Cache only invoke on main thread.");
                if (sRaycastCache == null)
                    sRaycastCache = new RaycastHit[32];
                return sRaycastCache;
            }
        }

        public static RaycastHit[] RaycastCache2
        {
            get
            {
                if (!MainThread.IsMainThread)
                    throw new System.Exception("Raycast Cache only invoke on main thread.");
                if (sRaycastCache2 == null)
                    sRaycastCache2 = new RaycastHit[32];
                return sRaycastCache2;
            }
        }

        static Collider[] sColliderCache;
        public static Collider[] ColliderCache
        {
            get
            {
                if (!MainThread.IsMainThread)
                    throw new System.Exception("Collider Cache only invoke on main thread.");
                if (sColliderCache == null)
                    sColliderCache = new Collider[128];
                return sColliderCache;
            }
        }

        readonly static HashSet<GameObject> sTargets = new HashSet<GameObject>();
        public static HashSet<GameObject> CheckTargets()
        {
            if (!MainThread.IsMainThread)
                throw new System.Exception("Check Targets Cache only invoke on main thread.");
            sTargets.Clear();
            return sTargets;
        }


        readonly static Stack<Transform> sTransStack = new Stack<Transform>(64);
        public static Stack<Transform> GetTransformStack()
        {
            if (!MainThread.IsMainThread)
                throw new System.Exception("Transform Stack Cache only invoke on main thread.");
            sTransStack.Clear();
            return sTransStack;
        }

        public static void Clear()
        {
            //lock (sLock)
            //{
            if (sRaycastCache != null)
            {
                for (int i = 0; i < sRaycastCache.Length; i++)
                    sRaycastCache[i] = default;
            }
            if (sColliderCache != null)
            {
                for (int i = 0; i < sColliderCache.Length; i++)
                    sColliderCache[i] = null;
            }
            if (sCacheLst != null)
            {
                for (int i = 0; i < sCacheLst.Count; i++)
                {
                    sCacheLst[i].list.Clear();
                    sCacheLst[i].hot = 0;
                }
            }
            //}
        }

        public static void SetLayerRecursive(this GameObject go, int layer)
        {
            var stack = GetTransformStack();
            stack.Clear();
            stack.Push(go.transform);
            while (stack.Count > 0)
            {
                var it = stack.Pop();
                it.gameObject.layer = layer;
                for (int i = 0; i < it.childCount; i++)
                {
                    var next = it.GetChild(i);
                    stack.Push(next);
                }
            }
        }
    }
}